package com.thouiot.modbus.component;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.ip.udp.UdpMaster;
import com.serotonin.modbus4j.serial.ascii.AsciiMaster;
import com.serotonin.modbus4j.serial.rtu.RtuMaster;
import com.thouiot.modbus.config.ModbusTypeEnum;
import com.thouiot.modbus.config.properties.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @description modbus master 构建工厂，根据配置文件构建modbus master
 *      目前会在第一次获取时构建，后续获取时直接返回
 * @author wangkai
 * @date 2025/04/10 14:17
 */
@Component
public class ModbusMasterHolder {

    @Resource
    private ModbusMasterProperties modbusMasterProperties;

    private final ConcurrentHashMap<String, ModbusMaster> modbusMasterMap = new ConcurrentHashMap<>();

    /**
     * 获取modbus master
     * @param name modbus主机名称
     * @return
     */
    public ModbusMaster getModbusMaster(String name, ModbusTypeEnum masterType) {
        if (modbusMasterMap.containsKey(name)) {
            return modbusMasterMap.get(name);
        }
        return modbusMasterMap.computeIfAbsent(name, key -> {
            if (masterType == ModbusTypeEnum.TCP) {
                return buildTcpMaster(name);
            } else if (masterType == ModbusTypeEnum.UDP) {
                return buildUdpMaster(name);
            } else if (masterType == ModbusTypeEnum.RTU) {
                return buildRtuMaster(name);
            } else if (masterType == ModbusTypeEnum.ASCII) {
                return buildAsciiMaster(name);
            } else {
                return null;
            }
        });
    }

    // 构建 tcp modbus master
    private ModbusMaster buildTcpMaster(String name) {
        List<ModbusMasterTcp> tcpMasters = modbusMasterProperties.getTcpMasters();
        if (CollectionUtils.isEmpty(tcpMasters)) {
            return null;
        }
        Map<String, ModbusMasterTcp> masterTcpMap = tcpMasters.stream().collect(Collectors.toMap(ModbusMasterTcp::getName, Function.identity(), (v1, v2) -> v1));
        if (!masterTcpMap.containsKey(name)) {
            return null;
        }
        ModbusMasterTcp masterTcp = masterTcpMap.get(name);
        IpParameters ipParameters = new IpParameters();
        ipParameters.setHost(masterTcp.getHost());
        ipParameters.setPort(masterTcp.getPort());
        ModbusMaster tcpMaster = new TcpMaster(ipParameters, masterTcp.isKeepAlive());
        try {
            tcpMaster.init();
        } catch (ModbusInitException e) {
            throw new RuntimeException(e);
        }
       return tcpMaster;
    }

    /**
     * 构建 udp modbus master
     * @param name
     * @return
     */
    private ModbusMaster buildUdpMaster(String name) {
        List<ModbusMasterUdp> udpMasters = modbusMasterProperties.getUdpMasters();
        if (CollectionUtils.isEmpty(udpMasters)) {
            return null;
        }
        Map<String, ModbusMasterUdp> masterUdpMap = udpMasters.stream().collect(Collectors.toMap(ModbusMasterUdp::getName, Function.identity(), (v1, v2) -> v1));
        if (!masterUdpMap.containsKey(name)) {
            return null;
        }
        ModbusMasterUdp masterUdp = masterUdpMap.get(name);
        IpParameters ipParameters = new IpParameters();
        ipParameters.setHost(masterUdp.getHost());
        ipParameters.setPort(masterUdp.getPort());
        ModbusMaster udpMaster = new UdpMaster(ipParameters);
        try {
            udpMaster.init();
        } catch (ModbusInitException e) {
            throw new RuntimeException(e);
        }
        return udpMaster;
    }


    /**
     * 构建 rtu modbus master
     * @param name
     * @return
     */
    private ModbusMaster buildRtuMaster(String name) {
        List<ModbusMasterRtu> rtuMasters = modbusMasterProperties.getRtuMasters();
        if (CollectionUtils.isEmpty(rtuMasters)) {
            return null;
        }
        Map<String, ModbusMasterRtu> masterRtuMap = rtuMasters.stream().collect(Collectors.toMap(ModbusMasterRtu::getName, Function.identity(), (v1, v2) -> v1));
        if (!masterRtuMap.containsKey(name)) {
            return null;
        }
        ModbusMasterRtu masterRtu = masterRtuMap.get(name);
        ModbusMaster rtuMaster = new RtuMaster(masterRtu);
        try {
            rtuMaster.init();
        } catch (ModbusInitException e) {
            throw new RuntimeException(e);
        }
        return rtuMaster;
    }

    /**
     * 构建 ascii modbus master
     * @param name
     * @return
     */
    private ModbusMaster buildAsciiMaster(String name) {
        List<ModbusMasterAscii> rtuMasters = modbusMasterProperties.getAsciiMasters();
        if (CollectionUtils.isEmpty(rtuMasters)) {
            return null;
        }
        Map<String, ModbusMasterAscii> masterRtuMap = rtuMasters.stream().collect(Collectors.toMap(ModbusMasterAscii::getName, Function.identity(), (v1, v2) -> v1));
       if (!masterRtuMap.containsKey(name)) {
           return null;
       }
       ModbusMasterAscii masterRtu = masterRtuMap.get(name);
       ModbusMaster asciiMaster = new AsciiMaster(masterRtu);
       try {
           asciiMaster.init();
       } catch (ModbusInitException e) {
           throw new RuntimeException(e);
       }
       return asciiMaster;
    }


    /**
     * 关闭所有modbus master
     */
    public void closeModbusMasters() {
        modbusMasterMap.forEach((k, v) -> v.destroy());
    }

    public void initModbusMasters() {
        if (!modbusMasterProperties.isEnableInit()) {
            return;
        }
        Optional.ofNullable(modbusMasterProperties.getUdpMasters())
                .ifPresent(udpMasters -> udpMasters.forEach(
                        udpMaster -> getModbusMaster(udpMaster.getName(), ModbusTypeEnum.UDP)));
        Optional.ofNullable(modbusMasterProperties.getTcpMasters())
                .ifPresent(tcpMasters -> tcpMasters.forEach(
                        tcpMaster -> getModbusMaster(tcpMaster.getName(), ModbusTypeEnum.TCP)));
        Optional.ofNullable(modbusMasterProperties.getRtuMasters())
                .ifPresent(rtuMasters -> rtuMasters.forEach(
                        rtuMaster -> getModbusMaster(rtuMaster.getName(), ModbusTypeEnum.RTU)));
        Optional.ofNullable(modbusMasterProperties.getAsciiMasters())
                .ifPresent(asciiMasters -> asciiMasters.forEach(
                        asciiMaster -> getModbusMaster(asciiMaster.getName(), ModbusTypeEnum.ASCII)));
    }
}
